package com.ujcms.cms.core.web.api;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.fasterxml.jackson.annotation.JsonView;
import com.nimbusds.oauth2.sdk.util.JSONUtils;
import com.ujcms.cms.core.component.ViewCountService;
import com.ujcms.cms.core.domain.*;
import com.ujcms.cms.core.service.ArticleService;
import com.ujcms.cms.core.service.BlockItemService;
import com.ujcms.cms.core.service.ChannelBufferService;
import com.ujcms.cms.core.service.ChannelService;
import com.ujcms.cms.core.service.args.ArticleArgs;
import com.ujcms.cms.core.service.args.BlockItemArgs;
import com.ujcms.cms.core.service.args.ChannelArgs;
import com.ujcms.cms.core.support.Contexts;
import com.ujcms.cms.core.web.directive.ArticleListDirective;
import com.ujcms.cms.core.web.directive.ChannelListDirective;
import com.ujcms.cms.core.web.support.Directives;
import com.ujcms.cms.core.web.support.SiteResolver;
import com.ujcms.commons.query.QueryUtils;
import com.ujcms.commons.web.Views;
import com.ujcms.commons.web.exception.Http404Exception;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.lang.Nullable;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;

import static com.ujcms.cms.core.support.UrlConstants.API;
import static com.ujcms.cms.core.support.UrlConstants.FRONTEND_API;
import static com.ujcms.commons.db.MyBatis.springPage;
import static com.ujcms.commons.query.QueryUtils.QUERY_PREFIX;
import static com.ujcms.commons.query.QueryUtils.getQueryMap;

/**
 * 首页 接口
 *
 * @author PONY
 */
@Tag(name = "CommonController", description = "首页接口")
@RestController
@RequestMapping({API + "/common", FRONTEND_API + "/common"})
public class CommonController {
    private static final Logger logger = LoggerFactory.getLogger(CommonController.class);
    private final SiteResolver siteResolver;
    private final ChannelService channelService;
    private final BlockItemService blockItemService;
    private final ArticleService articleService;

    public CommonController(SiteResolver siteResolver, ChannelService channelService,BlockItemService blockItemService,ArticleService articleService) {
        this.siteResolver = siteResolver;
        this.channelService = channelService;
        this.blockItemService = blockItemService;
        this.articleService = articleService;
    }

    @Operation(summary = "获取首页菜单")
    @Parameter(in = ParameterIn.QUERY, name = "siteId", description = "站点ID。默认为当前站点",
            schema = @Schema(type = "integer", format = "int64"))
    @Parameter(in = ParameterIn.QUERY, name = "parent", description = "上级栏目别名",
            schema = @Schema(type = "string"))
    @Parameter(in = ParameterIn.QUERY, name = "parentId", description = "上级栏目ID",
            schema = @Schema(type = "integer", format = "int64"))
    @Parameter(in = ParameterIn.QUERY, name = "isNav", description = "是否导航。如：`true` `false`",
            schema = @Schema(type = "boolean"))
    @Parameter(in = ParameterIn.QUERY, name = "isAllowSearch", description = "是否可搜索。如：`true` `false`",
            schema = @Schema(type = "boolean"))
    @Parameter(in = ParameterIn.QUERY, name = "isIncludeChildren", description = "是否包含子栏目。如：`true` `false`，默认`false`",
            schema = @Schema(type = "boolean"))
    @Parameter(in = ParameterIn.QUERY, name = "offset", description = "从第几条数据开始获取。默认为0，即从第一条开始获取",
            schema = @Schema(type = "integer", format = "int32"))
    @Parameter(in = ParameterIn.QUERY, name = "limit", description = "共获取多少条数据。最大不能超过1000",
            schema = @Schema(type = "integer", format = "int32"))
    @GetMapping("/menu")
    @ResponseBody
    public String menu(HttpServletRequest request) {
        Site site = siteResolver.resolve(request);
        Map<String, String> params = QueryUtils.getParams(request.getQueryString());
        ChannelArgs args = ChannelArgs.of(QueryUtils.getQueryMap(params, QUERY_PREFIX));
        ChannelListDirective.assemble(args, params, site.getId(), channelService);
        args.customsQueryMap(QueryUtils.getCustomsQueryMap(params));
        List<Channel> list = ChannelListDirective.selectList(channelService, args, params);

        JSONArray array = new JSONArray();
        for (Channel channel : list) {
            Channel bean = channelService.select(channel.getId());
            JSONObject object = new JSONObject();
            object.put("id",bean.getId());
            object.put("name",bean.getName());
            JSONObject obj = JSONObject.parseObject(bean.getMainsJson());
            if(null != obj && obj.containsKey("channel_desc")){
                object.put("desc",obj.getString("channel_desc"));
            }
            array.add(object);
        }
        return array.toJSONString();
    }

    @Operation(summary = "轮播图")
    @Parameter(in = ParameterIn.QUERY, name = "siteId", description = "站点ID。默认为当前站点",
            schema = @Schema(type = "integer", format = "int64"))
    @Parameter(in = ParameterIn.QUERY, name = "blockId", description = "模块ID",
            schema = @Schema(type = "long"))
    @ResponseBody
    @GetMapping("/blockItem")
    public String blockItem(@Nullable Long blockId, HttpServletRequest request) {
        Site site = siteResolver.resolve(request);
        BlockItemArgs args = BlockItemArgs.of(getQueryMap(request.getQueryString()))
                .siteId(site.getId())
                .blockId(blockId);
        List<BlockItem> list =  blockItemService.selectList(args);

        JSONArray array = new JSONArray();
        for (BlockItem channel : list) {
            JSONObject object = new JSONObject();
            object.put("id",channel.getId());
            object.put("title",channel.getTitle());
            object.put("subtitle",channel.getSubtitle());
            object.put("description",channel.getDescription());
            object.put("image",site.getDomain()+channel.getImage());
            object.put("url",channel.getUrl());
            array.add(object);
        }
        return array.toJSONString();
    }
    @Operation(summary = "获取文章分页")
    @Parameter(in = ParameterIn.QUERY, name = "siteId", description = "站点ID。默认为当前站点",
            schema = @Schema(type = "integer", format = "int64"))
    @Parameter(in = ParameterIn.QUERY, name = "channelId", description = "栏目ID",
            schema = @Schema(type = "integer", format = "int64"))
    @Parameter(in = ParameterIn.QUERY, name = "isIncludeSubChannel", description = "是否包含子栏目的文章。如：`true` `false`，默认`true`",
            schema = @Schema(type = "boolean"))
    @Parameter(in = ParameterIn.QUERY, name = "page", description = "第几页",
            schema = @Schema(type = "integer", format = "int32"))
    @Parameter(in = ParameterIn.QUERY, name = "pageSize", description = "每页多少条数据。最大不能超过1000",
            schema = @Schema(type = "integer", format = "int32"))
    @ResponseBody
    @GetMapping("/page")
    public String page(HttpServletRequest request) {
        Page<Article> pages = query(request, (args, params) -> {
            int page = Directives.getPage(params);
            int pageSize = Directives.getPageSize(params);
            return springPage(articleService.selectPage(args, page, pageSize));
        });

        JSONObject obj = new JSONObject();
        JSONArray array = new JSONArray();
        for (Article article : pages.getContent()) {
            JSONObject object = new JSONObject();
            object.put("id",article.getId());
            object.put("title",article.getTitle());
            object.put("description",article.getDescription());
            object.put("publishDate",article.getPublishDate());
            object.put("imageList",article.getImageList());
            object.put("image",article.getImage());
            object.put("url",article.getUrl());
            array.add(object);
        }
        obj.put("list",array);
        obj.put("total",pages.getTotalElements());
        obj.put("page",pages.getNumber()+1);
        obj.put("size",pages.getSize());
        return JSON.toJSONString(obj, SerializerFeature.WriteNullStringAsEmpty,
                SerializerFeature.WriteNullListAsEmpty,
                SerializerFeature.WriteNullNumberAsZero);
    }

    @Operation(summary = "获取文章对象")
    @ApiResponses(value = {@ApiResponse(description = "文章对象")})
    @GetMapping("/article/{id:[\\d]+}")
    @ResponseBody
    public String article(@Parameter(description = "文章ID") @PathVariable Long id) {
        Article article = articleService.select(id);
        if (article == null) {
            throw new Http404Exception("Article not found. ID: " + id);
        }

        logger.info(JSONObject.toJSONString(article));

        JSONObject object = new JSONObject();
        object.put("id",article.getId());
        object.put("title",article.getTitle());
        object.put("description",article.getDescription());
        object.put("plainText",article.getPlainText());
        object.put("text",article.getText());

        object.put("publishDate",article.getPublishDate());
        object.put("imageList",article.getImageList());
        object.put("image",article.getImage());
        object.put("url",article.getUrl());
        return object.toString();
    }

    private <T> T query(HttpServletRequest request, BiFunction<ArticleArgs, Map<String, String>, T> handle) {
        Site site = siteResolver.resolve(request);
        Map<String, String> params = QueryUtils.getParams(request.getQueryString());
        ArticleArgs args = ArticleArgs.of(QueryUtils.getQueryMap(params, QUERY_PREFIX));
        ArticleListDirective.assemble(args, params, site.getId(), channelService);
        args.customsQueryMap(QueryUtils.getCustomsQueryMap(params));
        return handle.apply(args, params);
    }

}
